package com.qfedu.edu.searchservice.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qfedu.edu.bo.DbClassData;
import com.qfedu.edu.constance.ElasticsearchConstance;
import com.qfedu.edu.exception.BusinessException;
import com.qfedu.edu.po.Class;
import com.qfedu.edu.result.ResponseCode;
import com.qfedu.edu.searchservice.service.SearchService;
import com.qfedu.edu.searchservice.utils.ESUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xiaobobo
 * @title: SearchServiceImpl
 * @projectName seckill-fy-2401-parent
 * @description: TODO
 * @date 2024/11/7  11:28
 */
@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Override
    public void sync(String str) throws IOException {
        log.info("收到的数据是:"+str);
        //下面就是要将这个数据封装成对象去使用...
        //首先将这个数据弄成JSONObject对象
        JSONObject jsonObject = JSON.parseObject(str);
        //接下就可以获取当前操作的是哪一张表  以及 这个操作的类型是啥
        String tableName = jsonObject.getString("table");
        String type = jsonObject.getString("type");
        //剩余的数据就可以封装成一个对象了...
        DbClassData dbClassData=getDbClassData(jsonObject,tableName,type);
        //接下来就可以根据数据库的操作类型来同步这个数据了...
        switch (type){
            case "INSERT":
                addDataToEs(dbClassData);
                break;
            case "UPDATE":
                updateDataToEs(dbClassData);
                break;
            case "DELETE":
                deleteDataToEs(dbClassData);
                break;
        }
    }

    @Override
    public void createIndexAndType(String indexName, String typeName) throws IOException {
        //准备创建索引的分片和副本设置
        Settings settings = Settings.builder()
                .put("number_of_shards", 5)
                .put("number_of_replicas", 1)
                .build();

        //准备表中字段的数据类型
        XContentBuilder xContentBuilder = JsonXContent.contentBuilder()
                .startObject()
                .startObject("properties")

                .startObject("class_id")
                .field("type", "keyword")
                .endObject()

                .startObject("class_des")
                .field("type", "text")
                .field("index", "true")
                .field("store", "false")
                .endObject()

                .startObject("photo")
                .field("type", "keyword")
                .field("index", "true")
                .field("store", "false")
                .endObject()


                .startObject("price")
                .field("type", "integer")
                .field("index", "true")
                .field("store", "false")
                .endObject()


                .startObject("sec_kill_price")
                .field("type", "integer")
                .field("index", "true")
                .field("store", "false")
                .endObject()


                .startObject("start_time")
                .field("type", "date")
                .field("index", "true")
                .field("store", "false")
                .endObject()


                .startObject("end_time")
                .field("type", "date")
                .field("index", "true")
                .field("store", "false")
                .endObject()

                .startObject("model")
                .field("type", "integer")
                .field("index", "true")
                .field("store", "false")
                .endObject()

                .startObject("create_time")
                .field("type", "date")
                .field("index", "true")
                .field("store", "false")
                .endObject()

                .startObject("update_time")
                .field("type", "date")
                .field("index", "true")
                .field("store", "false")
                .endObject()


                .startObject("version")
                .field("type", "integer")
                .field("index", "true")
                .field("store", "false")
                .endObject()

                .startObject("deleted")
                .field("type", "integer")
                .field("index", "true")
                .field("store", "false")
                .endObject()

                .endObject()
                .endObject();
        //将settings和mapping映射到一个Request对象中去
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName)
                .settings(settings)
                .mapping(typeName, xContentBuilder);

        //接下来才是向服务器发送请求创建索引  类型 字段
        CreateIndexResponse createIndexResponse = ESUtils.restHighLevelClient().indices().create(createIndexRequest, RequestOptions.DEFAULT);

        log.info("服务器的确认状态:" + createIndexResponse.isAcknowledged());
    }

    @Override
    public List<Class> listByPage(Integer currentPage, Integer pageSize) throws IOException {
        if(null==currentPage||null==pageSize){
          throw new BusinessException(ResponseCode.PARAM_EXCEPTION);
        }
        //执行到这里 说明参数无误
        //调用ES的API完成数据的查询

        //申明搜索的对象
        SearchRequest searchRequest = new SearchRequest(ElasticsearchConstance.getIndexName());
        //设置查询的类型
        searchRequest.types(ElasticsearchConstance.getTypeName());
        //构建查询的条件

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //从哪里开始查询数据
        //这里要计算从哪里查询  查询多少条数据
        int start=(currentPage-1)*pageSize;
        searchSourceBuilder.from(start);
        //查询多少条数据
        searchSourceBuilder.size(pageSize);
        //构建条件
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());

        searchRequest.source(searchSourceBuilder);

        //接下来发送请求查询数据
        SearchResponse search = ESUtils.restHighLevelClient().search(searchRequest, RequestOptions.DEFAULT);
        //接下来遍历这个数据


        List<Class> classList=new ArrayList<>();

        //4. 获取到_source中的数据，并展示
        for (SearchHit hit : search.getHits().getHits()) {
            //每循环一次 实际上 就是一个文档数据
            Map<String, Object> result = hit.getSourceAsMap();
            //要将map转换成对象
            ObjectMapper objectMapper = new ObjectMapper();
            Class clazz = objectMapper.convertValue(result, Class.class);
            classList.add(clazz);
            System.out.println(result);
        }
        return classList;
    }

    /**
     * 删除这个ES中的文档
     * @param dbClassData
     */
    private void deleteDataToEs(DbClassData dbClassData) throws IOException {
        //删除数据从ES中
        Class afterClass = dbClassData.getAfterClass();
        String classId = afterClass.getClassId();
        //接下来按照id删除这个数据
        DeleteRequest deleteRequest = new DeleteRequest(ElasticsearchConstance.getIndexName(), ElasticsearchConstance.getTypeName(), classId);
        //接下来删除数据
        DeleteResponse delete = ESUtils.restHighLevelClient().delete(deleteRequest, RequestOptions.DEFAULT);
        log.info("删除的结果是:{}",delete.getResult().name());
    }

    /**
     * 修改ES中文档
     * @param dbClassData
     */
    private void updateDataToEs(DbClassData dbClassData) throws IOException {
        Class afterClass = dbClassData.getAfterClass();

        ObjectMapper objectMapper = new ObjectMapper();

        // 将对象转换为Map
        Map<String, Object> map = objectMapper.convertValue(afterClass, Map.class);

        //构建咋们的请求对象
        UpdateRequest updateRequest = new UpdateRequest(ElasticsearchConstance.getIndexName(), ElasticsearchConstance.getTypeName(),afterClass.getClassId());
        updateRequest.doc(map);

        //下面开始调用
        UpdateResponse update = ESUtils.restHighLevelClient().update(updateRequest, RequestOptions.DEFAULT);
        log.info("修改的结果是:{}",update.getResult().name());

    }

    /**
     * 向ES中写入文档
     * @param dbClassData
     */
    private void addDataToEs(DbClassData dbClassData) throws IOException {
        //首先拿到这个数据
        Class afterClass = dbClassData.getAfterClass();
        //将数据弄成json格式
        String data = JSON.toJSONString(afterClass);
        //准备请求对象
        IndexRequest indexRequest = new IndexRequest(ElasticsearchConstance.getIndexName(), ElasticsearchConstance.getTypeName(), afterClass.getClassId());
        //设置数据
        indexRequest.source(data, XContentType.JSON);
        //发送请求了
        IndexResponse index = ESUtils.restHighLevelClient().index(indexRequest, RequestOptions.DEFAULT);
        log.info("返回的状态是:{}",index.status().getStatus());

    }

    /**
     * 这个就是将传输来的数据 弄成对象
     * @param jsonObject
     * @param tableName
     * @param type
     * @return
     */
    private DbClassData getDbClassData(JSONObject jsonObject,
                                       String tableName,
                                       String type) {
        JSONArray jsonArray = (JSONArray) jsonObject.get("data");
        JSONObject json = (JSONObject) jsonArray.get(0);
        //属性没对应上
        Class aClass = new Class();
        aClass.setClassId(json.getString("class_id"));
        aClass.setClassName(json.getString("class_name"));
        aClass.setClassDesc(json.getString("class_desc"));
        aClass.setPhoto(json.getString("photo"));
        aClass.setPrice(json.getInteger("price"));
        aClass.setSecKillPrice(json.getInteger("sec_kill_price"));
        aClass.setStartTime(json.getDate("start_time"));
        aClass.setEndTime(json.getDate("end_time"));
        aClass.setModel(json.getInteger("model"));

        aClass.setStartTime(json.getDate("start_time"));
        aClass.setCreateTime(json.getDate("create_time"));
        aClass.setUpdateTime(json.getDate("update_time"));
        aClass.setVersion(json.getInteger("version"));
        aClass.setDeleted(json.getInteger("deleted"));

        DbClassData dbClassDataBo = new DbClassData();
        dbClassDataBo.setTabName(tableName);
        dbClassDataBo.setEventType(type);
        dbClassDataBo.setAfterClass(aClass);
        return dbClassDataBo;
    }
}
